﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Xml.Linq;
using CoolCode.Data.Linq;
using CoolCode.Reflection;

namespace CoolCode.Data.Xml
{
    public class XmlContext : IDataContext, IDisposable
    {

        /// <summary>
        /// Called by QueryProvider to execute queries
        /// </summary>
        /// <param name="expression">ExpressionTree to parse</param>
        /// <returns>list of objects with query results</returns>
        //internal IQueryable Execute(Expression expression)
        //{
        //    var type = TypeSystem.GetElementType(expression.Type); //(expression as MethodCallExpression ).Arguments [1].Type ;
        //    var table = TableFactory.Instance.GetTable(Path, type);
        //    var className = table.Name;
        //    var root = table.Root;
        //    var fileName = table.FileName;
        //    var lambda = (expression as MethodCallExpression).Arguments[1];
        //    ConditionBuilder cond = new ConditionBuilder(lambda);
        //    var result = (cond.Result as UnaryExpression).Operand as System.Linq.Expressions.Expression<Func<XElement, bool>>;
        //    var query = root.Descendants(className).Where(result.Compile());
        //    var xList = from x in query select XmlConvert.Xml2Entity(x, type);
        //    return xList.AsQueryable();
        //}

        public IQueryable Execute(Expression expression)
        {
            Type  type =  expression.Type.GetElementType(); //(expression as MethodCallExpression ).Arguments [1].Type ;
            //var table = TableFactory.Instance.GetTable(Path, type); 
            var table = TableProvider.Instance.GetTable(this, type);
            var className = table.ClassName;
            var root = table.Root;
            bool argFlg = ((expression as MethodCallExpression).Arguments.Count > 1);
            var lambda = argFlg ? (expression as MethodCallExpression).Arguments[1] : (expression as MethodCallExpression).Arguments[0];
            ConditionBuilder cond = new ConditionBuilder(lambda);
            var result = (cond.Result as UnaryExpression).Operand as System.Linq.Expressions.Expression<Func<XElement, bool>>;
            var query = root.Descendants(className).Where(result.Compile());
            var xList = from x in query select XmlConvert.Xml2Entity(x, type);
            //foreach (var q in xList)
            //    ObjectDumper.Write(q);
            return xList.AsQueryable();
        }

        public IEnumerable<T> Execute<T>(Expression expression)
        {
            //var type = TypeHelper.GetElementType(expression.Type); //(expression as MethodCallExpression ).Arguments [1].Type ;
            ////var table = TableFactory.Instance.GetTable<T>(Path);
            //var table = TableProvider.Instance.GetTable<T>(this );
            //var className = table.ClassName ;
            //var root = table.Root; 
            //var lambda = (expression as MethodCallExpression).Arguments[1];
            //ConditionBuilder cond = new ConditionBuilder(lambda);
            //var result = (cond.Result as UnaryExpression).Operand as System.Linq.Expressions.Expression<Func<XElement, bool>>;
            //var query = root.Descendants(className).Where(result.Compile());
            var query = Query<T>(expression, this);
            var xList = from x in query select XmlConvert.Xml2Entity<T>(x);
            //foreach (var q in xList)
            //    CoolCode.Share.ObjectDumper.Write(q);
            return xList;
        }

        internal static IEnumerable<XElement> Query<T>(Expression expression, IDataContext context)
        {
            var type =  expression.Type.GetElementType(); //(expression as MethodCallExpression ).Arguments [1].Type ;
            //var table = TableFactory.Instance.GetTable<T>(Path);
            var table = TableProvider.Instance.GetTable<T>(context);
            var className = table.ClassName;
            var root = table.Root;
            Expression lambda;
            // lambda = (expression as MethodCallExpression).Arguments[1];
            lambda = (expression as MethodCallExpression);
            Expression<Func<XElement, bool>> result;
            if (lambda == null)
            {
                lambda = expression;
                ConditionBuilder cond = new ConditionBuilder(lambda);
                result = cond.Result as Expression<Func<XElement, bool>>;
            }
            else
            {
                lambda = (expression as MethodCallExpression).Arguments[1];
                ConditionBuilder cond = new ConditionBuilder(lambda);
                result = (cond.Result as UnaryExpression).Operand as Expression<Func<XElement, bool>>;
            }
            //ConditionBuilder cond = new ConditionBuilder(lambda);
            //var result = (cond.Result as UnaryExpression).Operand as  Expression<Func<XElement, bool>>;
            if (result == null)
            {
                return root.Descendants(className);
            }
            var query = root.Descendants(className).Where(result.Compile());
            return query;
        }


        public XmlContext()
        {

        }

        public XmlContext(string connectionString)
        {
            this.ConnectionString = connectionString.EndsWith(@"\") ? connectionString : connectionString + @"\";
        }

        #region IDataContext Members

        public ITable<T> GetTable<T>()
        {
            return TableProvider.Instance.GetTable<T>(this);
        }

        public void SubmitChanges()
        {
            var tables = TableProvider.Instance.GetAllTables();
            var en = tables.GetEnumerator();
            while (en.MoveNext())
            {
                var table = en.Current.Value.XmlTable;
                if (table.IsChanged)
                {
                    table.Root.Save(en.Current.Value.FileName);
                    table.IsChanged = false;
                }
            }

        }

        public string ConnectionString
        {
            get;
            set;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            SubmitChanges();
        }

        #endregion


    }
}
